package org.datascooter.parser;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.datascooter.exception.EntityNotMappedException;
import org.datascooter.impl.Condition;
import org.datascooter.impl.DataSnip;
import org.datascooter.impl.LogicValue;
import org.datascooter.inface.IXMLReader;
import org.datascooter.utils.policy.SnipType;

/**
 * Purposed for parsing object-relational mapping from eclipse extension point org.datascooter.mapping
 * 
 * @author nemo
 * 
 */
public class DSQueryProvider extends AbstractProvider<DataSnip> {

	private Map<String, DataSnip> snipMap = new HashMap<String, DataSnip>();

	public DSQueryProvider(IXMLReader... readerArray) {
		super(readerArray);
	}

	@Override
	protected void processRoot(IXMLReader element) throws EntityNotMappedException, IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
		processQuery(element, null, null, null);
	}

	@SuppressWarnings("nls")
	public DataSnip processQuery(IXMLReader query, DataSnip dataSnip, String joinAttribute, Boolean joinFromChild) throws EntityNotMappedException {
		if (dataSnip != null) {
			if (joinAttribute != null) {
				dataSnip = dataSnip.fetchByResult(joinAttribute, query.getAttribute("entity_name"), joinFromChild);
			} else {
				dataSnip = dataSnip.fetch(query.getAttribute("entity_name"));
			}
		} else {
			dataSnip = new DataSnip(query.getAttribute("entity_name"), SnipType.valueOf(query.getAttribute("type")));
			snipMap.put(query.getAttribute("name"), dataSnip);
		}
		processWhere(dataSnip, query.getChildren("where"));
		processWhat(dataSnip, query.getChildren("what"));
		processJoin(dataSnip, query.getChildren("join"));
		processFetch(dataSnip, query.getChildren("fetch"));
		processFetchByResult(dataSnip, query.getChildren("fetch_by_result"));
		for (IXMLReader childQuery : query.getChildren("query")) {
			processQuery(childQuery, dataSnip, null, null);
		}
		return dataSnip;
	}

	public void processFetchByResult(DataSnip dataSnip, IXMLReader[] elements) throws EntityNotMappedException {
		if (elements.length > 0) {
			for (IXMLReader fetch : elements) {
				String fetchAttr = fetch.getAttribute("fetch_attribute");
				String joinFromChildStr = fetch.getAttribute("join_from_child");
				Boolean joinFromChild = ((joinFromChildStr != null) && (joinFromChildStr.equalsIgnoreCase("true"))) ? Boolean.TRUE : Boolean.FALSE;
				for (IXMLReader childFetch : fetch.getChildren("query")) {
					processQuery(childFetch, dataSnip, fetchAttr, joinFromChild);
				}
			}
		}
	}

	public void processWhere(DataSnip dataSnip, IXMLReader[] elements) {
		if (elements.length > 0) {
			for (IXMLReader where : elements) {
				for (IXMLReader condition : where.getChildren("logic_condition")) {
					dataSnip.where(processConditionLogic(condition));
				}
			}
		}
	}

	@SuppressWarnings("nls")
	public Condition processConditionLogic(IXMLReader logic) {
		List<Condition> childLogicList = new ArrayList<Condition>();
		for (IXMLReader childLogic : logic.getChildren("logic_condition")) {
			childLogicList.add(processConditionLogic(childLogic));
		}
		List<LogicValue> valueList = new ArrayList<LogicValue>();
		for (IXMLReader attributeValue : logic.getChildren("attribute_value")) {
			valueList.add(new LogicValue(attributeValue.getAttribute("attribute"), attributeValue.getAttribute("constraint"), attributeValue.getAttribute("value")));
		}
		return new Condition(logic.getAttribute("operation"), childLogicList, valueList);
	}

	public void processWhat(DataSnip dataSnip, IXMLReader[] elements) {
		if (elements.length > 0) {
			for (IXMLReader what : elements) {
				for (IXMLReader attribute : what.getChildren("attribute_value")) {
					dataSnip.what(attribute.getAttribute("name"), attribute.getAttribute("value"));
				}
			}
		}
	}

	public void processJoin(DataSnip dataSnip, IXMLReader[] elements) throws EntityNotMappedException {
		if (elements.length > 0) {
			for (IXMLReader join : elements) {
				String joinAttr = join.getAttribute("join_attribute");
				String joinFromChildStr = join.getAttribute("join_from_child");
				Boolean joinFromChild = ((joinFromChildStr != null) && (joinFromChildStr.equalsIgnoreCase("true"))) ? Boolean.TRUE : Boolean.FALSE;
				DataSnip join2 = null;
				if (joinAttr != null) {
					join2 = dataSnip.join(joinAttr, join.getAttribute("entity_name"), joinFromChild);
				} else {
					join2 = dataSnip.join(join.getAttribute("entity_name"));
				}
				processWhere(join2, join.getChildren("where"));
				processJoin(join2, join.getChildren("join"));
				processFetch(join2, join.getChildren("fetch"));
			}
		}
	}

	public void processFetch(DataSnip dataSnip, IXMLReader[] elements) throws EntityNotMappedException {
		if (elements.length > 0) {
			for (IXMLReader fetch : elements) {
				for (IXMLReader childFetch : fetch.getChildren("query")) {
					processQuery(childFetch, dataSnip, null, null);
				}
			}
		}
	}

	@Override
	public DataSnip getItem(String dname) {
		return snipMap.get(dname);
	}
}
